package small.common.core.spi;

import small.common.core.context.Lifecycle;
import small.common.core.util.ClassUtil;
import small.common.core.util.Holder;
import small.common.core.util.StringUtil;

import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.net.URL;
import java.nio.charset.StandardCharsets;
import java.util.*;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.atomic.AtomicBoolean;
import java.util.stream.Collectors;
import java.util.stream.Stream;

/**
 * spi加载器
 */
public class SpiLoader<T> {

    /**
     * 加载的程序
     */
    private static final ConcurrentHashMap<String, SpiLoader<?>> SPI_LOADER_MAP = new ConcurrentHashMap<>();

    /**
     * 是否加载的标志
     */
    private final AtomicBoolean loaded = new AtomicBoolean(false);

    /**
     * 加载的类
     */
    private final Holder<Map<String, Class<?>>> classMap = new Holder<>();

    /**
     * 单实例集合
     */
    private final ConcurrentHashMap<String, Holder<T>> singletonMap = new ConcurrentHashMap<>();

    /**
     * 默认的实例
     */
    private Class<? extends T> defaultClass = null;

    /**
     * 加载类
     */
    private Class<T> loader;

    /**
     * 加载类名称(加载文件名)
     */
    private String loaderName;

    /**
     * spi类加载的位置
     */
    private String[] directory;

    private SpiLoader(Class<T> loader, String loaderName, String[] directory) {
        this.loader = loader;
        this.loaderName = loaderName;
        this.directory = directory;

    }

    /**
     * 创建加载器
     *
     * @param loader    加载类
     * @param directory 加载的目录
     * @param <T>
     * @return
     */
    public static <T> SpiLoader<T> newInstance(Class<T> loader, String[] directory) {
        return newInstance(loader, loader.getName(), directory);
    }

    /**
     * 创建加载器
     *
     * @param loader     加载类
     * @param loaderName 加载类名称
     * @param directory  加载的目录
     * @param <T>
     * @return
     */
    @SuppressWarnings("unchecked")
    public static <T> SpiLoader<T> newInstance(Class<T> loader, String loaderName, String[] directory) {
        String name = loader.getName();
        SpiLoader<T> spiLoader = (SpiLoader<T>) SPI_LOADER_MAP.get(name);
        if (spiLoader == null) {
            synchronized (SpiLoader.class) {
                spiLoader = (SpiLoader<T>) SPI_LOADER_MAP.get(name);
                if (spiLoader == null) {
                    SPI_LOADER_MAP.putIfAbsent(name, new SpiLoader<T>(loader, loaderName, directory));
                    spiLoader = (SpiLoader<T>) SPI_LOADER_MAP.get(name);
                }
            }

        }
        return spiLoader;
    }

    @SuppressWarnings("unchecked")
    public T getInstance(Class<? extends T> clazz) {
        String className = getClassName(clazz);
        return getInstance(className);
    }

    /**
     * 获取所有加载的实例
     * @return
     */
    public List<T> getInstanceList() {
        List<T> instanceList = new LinkedList<>();
        for (String name : getLoaderClasses().keySet()) {
            instanceList.add(getInstance(name));
        }
        return instanceList;
    }

    /**
     * 获取实例
     *
     * @param name
     * @return
     */
    public T getInstance(String name) {
        if (StringUtil.isBlank(name)) {
            throw new IllegalArgumentException("Loader name == null");
        }
        Class<?> clazz = getLoaderClasses().get(name);
        if (clazz == null) {
            throw new IllegalArgumentException("Loader class type == null");
        }

        Spi spi = clazz.getAnnotation(Spi.class);
        boolean singleton = true;
        if (spi != null) {
            singleton = spi.isSingleton();
        }
        T instance = null;
        try {
            if (singleton) {
                final Holder<T> holder = getOrCreateHolder(name);
                instance = holder.get();
                if (instance == null) {
                    synchronized (holder) {
                        instance = holder.get();
                        if (instance == null) {
                            instance = createInstance(clazz);
                            holder.set(instance);
                        }
                    }
                }
            } else {
                instance = createInstance(clazz);
            }
        } catch (Throwable e) {
            e.printStackTrace();
        }
        return instance;
    }

    /**
     * 创建提供程序实例
     *
     * @param clz 类
     * @return Provider instance
     */
    private T createInstance(Class<?> clz) {
        T instance = null;
        try {
            instance = loader.cast(clz.newInstance());
            initExtension(instance);
        } catch (Throwable e) {
            e.printStackTrace();
        }
        return instance;
    }

    private void initExtension(T instance) {
        if (instance instanceof Lifecycle) {
            Lifecycle lifecycle = (Lifecycle) instance;
            lifecycle.initialize();
        }
    }

    /**
     * 获取加载类缓存
     *
     * @return
     */
    public Map<String, Class<?>> getLoaderClasses() {
        Map<String, Class<?>> classes = classMap.get();
        if (classes == null) {
            synchronized (classMap) {
                classes = classMap.get();
                if (classes == null) {
                    classes = load();
                    classMap.set(classes);
                }
            }
        }
        return classes;
    }


    /**
     * 移除类加载缓存
     *
     * @param clz
     */
    public void removeLoaderClass(Class<?> clz) {
        getLoaderClasses().remove(getClassName(clz));
    }

    /**
     * 加载类
     */
    @SuppressWarnings("unchecked")
    private Map<String, Class<?>> load() {

        //防止重复加载
        if (!this.loaded.compareAndSet(false, true)) {
            return classMap.get();
        }
        List<Class<?>> temClassList = new ArrayList();
        for (String dir : this.directory) {
            String fullFileName = dir + this.loaderName;
            ClassLoader classLoader = ClassUtil.getDefaultClassLoader();
            Enumeration<URL> urls = null;
            if (classLoader != null) {
                try {
                    urls = classLoader.getResources(fullFileName);
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
            while (urls != null && urls.hasMoreElements()) {
                URL url = urls.nextElement();
                InputStream in = null;
                BufferedReader br = null;
                try {
                    in = url.openStream();
                    br = new BufferedReader(new InputStreamReader(in, StandardCharsets.UTF_8));
                    String line;
                    while ((line = br.readLine()) != null) {
                        if (StringUtil.isBlank(line)) {
                            // 跳过空白行
                            continue;
                        }

                        line = line.trim();
                        int commentIndex = line.indexOf("#");
                        if (commentIndex == 0) {
                            // 跳过注释行
                            continue;
                        }
                        if (commentIndex > 0) {
                            line = line.substring(0, commentIndex);
                        }
                        line = line.trim();

                        Class<T> clazz = null;
                        try {
                            clazz = (Class<T>) Class.forName(line, false, classLoader);
                        } catch (ClassNotFoundException e) {
                            e.printStackTrace();
                        }

                        //不是定义类的子类
                        if (!loader.isAssignableFrom(clazz)) {
                            continue;
                        }

                        Spi spi = clazz.getAnnotation(Spi.class);
                        //设置默认加载类型
                        if (spi != null) {
                            //设置默认加载类
                            if (spi.isDefault() && defaultClass == null) {
                                defaultClass = clazz;
                            }
                        }
                        temClassList.add(clazz);
                    }
                } catch (IOException e) {
                    e.printStackTrace();
                } finally {
                    try {
                        in.close();
                        br.close();
                    } catch (IOException e) {
                        e.printStackTrace();
                    }
                }
            }


        }
        List<Class<?>> sortedList = temClassList.stream().sorted((o1, o2) -> {
            Spi spi1 = o1.getAnnotation(Spi.class);
            int order1 = spi1 == null ? 0 : spi1.order();

            Spi spi2 = o2.getAnnotation(Spi.class);
            int order2 = spi2 == null ? 0 : spi2.order();

            return Integer.compare(order1, order2);
        }).collect(Collectors.toList());

        LinkedHashMap<String, Class<?>> linkedHashMap = new LinkedHashMap<>();
        for (Class<?> aClass : sortedList) {
            linkedHashMap.put(getClassName(aClass),aClass);
        }
        return linkedHashMap;
    }


    public String getClassName(Class<?> clz) {
        String clzName = clz.getName();
        Spi spi = clz.getAnnotation(Spi.class);
        //设置默认加载类型
        if (spi != null) {
            //设置名称
            if (StringUtil.isNotBlank(spi.value())) {
                clzName = spi.value();
            }
        }
        return clzName;
    }

    /**
     * 获取对象锁
     *
     * @param name
     * @return
     */
    private Holder<T> getOrCreateHolder(String name) {
        Holder<T> holder = singletonMap.get(name);
        if (holder == null) {
            singletonMap.putIfAbsent(name, new Holder<>());
            holder = singletonMap.get(name);
        }
        return holder;
    }
}
